import { decode, encode } from "cbor-x";

const ORIGIN = window.location.origin;
let user_name = "";
let user_pwd = "";
const USER_NAME = "user_name";
const USER_PWD = "user_pwd";

export const API_URL = `${ORIGIN}/api`;
export const MSG_URL = `${ORIGIN}/msg`;

export function setLogin(name: string, pwd: string) {
    user_name = name;
    user_pwd = pwd;
    localStorage.setItem(USER_NAME, user_name);
    localStorage.setItem(USER_PWD, user_pwd);
}

export function loadLogin() {
    const name = localStorage.getItem(USER_NAME);
    const pwd = localStorage.getItem(USER_PWD);
    if (name != null) {
        user_name = name;
    }
    if (pwd != null) {
        user_pwd = pwd;
    }
}

export class Resp {
    readonly resp: Response;

    constructor(resp: Response) {
        this.resp = resp;
    }

    async objectUrl() {
        const body = await this.resp.arrayBuffer();
        return URL.createObjectURL(new Blob([body]));
    }

    async parse<T>() {
        const body = await this.resp.arrayBuffer();
        const data = new Uint8Array(body);
        return decode(data) as T;
    }
}

interface ErrInfo {
    err_code: number,
    err_msg: string,
}

function parseXHR(xhr: XMLHttpRequest) {
    if (xhr.status != 200) {
        throw xhr.statusText;
    }
    const head = xhr.getResponseHeader('resp')
    if (head == null) {
        throw 'no resp';
    }
    if (head != "ok") {
        const data = xhr.response as ArrayBuffer;
        const errInfo = decode(new Uint8Array(data)) as ErrInfo;
        throw `${errInfo.err_code}=>${errInfo.err_msg}`;
    }
    return xhr.response as ArrayBuffer;
}

async function parseResp(resp: Response) {
    if (resp.status != 200) {
        throw resp.statusText;
    }

    const ret = resp.headers.get('resp')
    if (ret == null) {
        throw 'NOT RESP';
    }

    if (ret != 'ok') {
        const body = new Uint8Array(await resp.arrayBuffer());
        const errInfo = decode(body) as ErrInfo;
        throw `${errInfo.err_code}=>${errInfo.err_msg}`;
    }

    return new Resp(resp);
}

class Service {
    readonly base: string;

    constructor(base: string) {
        this.base = base;
    }

    async post<T>(url: string, body?: T | Uint8Array, headers?: Record<string, string>) {
        let data: Uint8Array | undefined = undefined;
        if (body instanceof Uint8Array) {
            data = body;
        } else if (body == undefined) {
            data = undefined;
        } else {
            data = encode(body);
        }
        if (headers == null) {
            headers = {
                USER_NAME: user_name,
                USER_PWD: user_pwd
            }
        } else {
            headers[USER_NAME] = user_name;
            headers[USER_PWD] = user_pwd;
        }

        const resp = await fetch(`${this.base}${url}`, {
            method: 'POST',
            body: data,
            headers
        });
        return await parseResp(resp);
    }

    async get(url: string) {
        const resp = await fetch(`${this.base}${url}`, {
            method: 'GET',
        });
        return await parseResp(resp);
    }

    post2<T>(url: string, body: T | Uint8Array, progress: (n: number) => void) {
        let data: Uint8Array | null = null;
        if (body instanceof Uint8Array) {
            data = body
        } else {
            data = encode(body);
        }
        const xhr = new XMLHttpRequest();
        xhr.open('POST', `${this.base}${url}`, true);
        xhr.setRequestHeader(USER_NAME, user_name);
        xhr.setRequestHeader(USER_PWD, user_pwd);
        xhr.upload.onprogress = function (event) {
            if (event.lengthComputable) {
                progress(Math.floor(event.loaded * 100 / event.total));
            }
        };
        xhr.responseType = 'arraybuffer';
        return new Promise<XMLHttpRequest>((resolve, reject) => {
            xhr.onload = function () {
                resolve(xhr);
            };
            xhr.onerror = function () {
                reject('网络错误');
            };
            xhr.send(data);
        });
    }

    async postWithProg<T, R>(url: string, body: T | Uint8Array, progress: (n: number) => void) {
        const xhr = await this.post2(url, body, progress);
        const buf = parseXHR(xhr);
        const respBody = new Uint8Array(buf);
        return decode(respBody) as R;
    }

}

const ApiService = new Service(API_URL);
export const MsgService = new Service(MSG_URL);

export async function post<T>(url: string, body?: T | Uint8Array) {
    return ApiService.post(url, body);
}

export async function get(url: string) {
    return ApiService.get(url);
}

export async function postWithProg<T, R>(url: string, body: T | Uint8Array, progress: (n: number) => void) {
    return ApiService.postWithProg<T, R>(url, body, progress);
}

